Матч 399, Танцы на вечеринке (DancingParty)

Дивизион 1, Уровень 3

 

На вечеринку приглашены n мальчиков и n девочек. Они хотят танцевать несколько раундов. В каждом раунде гости делятся на n танцующих пар. Каждый гость должен быть в некоторой паре, каждая пара должна состоять из одного мальчика и одной девочки. В каждом раунде каждый мальчик должен танцевать с другой девочкой. Некоторые мальчики и девочки не нравятся друг другу. Каждый мальчик может танцевать не более чем с k девочками, которые ему не нравятся. Аналогично каждая девочка может танцевать не более чем с k мальчиками, которые ей не нравятся.

Массив likes описывает отношения между мальчиками и девочками: likes[i][j] = ‘Y’, если i - ому мальчику нравится j - ая девочка. Иначе likes[i][j] = ‘N’. Какое наибольшее количество раундов мальчики и девочки смогут танцевать?

 

Класс: DancingParty

Метод: int maxDances(vector<string>    likes, int k)

Ограничения: likes содержит от 1 до 50 элементов, likes[i] содержит n символов ‘Y’ и ‘N’ (n - количество строк в likes), 0 ≤ k ≤ 50.

 

Вход. Массив likes, описывающий отношения между гостями и целое число k.

 

Выход. Наибольшее количество раундов, которое мальчики и девочки смогут танцевать.

 

Пример входа

likes

k

{"YYY", "YYY", "YYY"}

0

{"YYY", "YYN", "YNY"}

0

{"YN", "YN"}

1

 

Пример выхода

3

2

1

 

 

РЕШЕНИЕ

максимальный поток

 

Рассмотрим следующую задачу: могут ли танцы продолжаться в точности m раундов? Если мы сможем ответить на этот вопрос, то бинарным поиском найдем наибольшее m, для которого проведение танцев возможно.

 Построим граф, имеющий один исток и один сток (черные вершины). Красные вершины представляют мальчиков, серые – девочек. Если мальчик и девочка нравятся друг другу, то проведем между ними ребро единичной пропускной способности (на рисунке такими являются два ребра – верхнее и нижнее). Иначе добавим синие и зеленые вершины как показано на рисунке и установим пропускную способность ребер между соответствующими синими и зелеными вершинами равную 1.

Синие и зеленые вершины образуют “защитный” уровень. Связь мальчиков с девочками, которые друг другу не нравятся, будет проходить по ребрам защитного уровня. Каждый мальчик может танцевать не более чем с k девочками, которые ему не нравятся. Установим пропускную способность ребер между красными и синими, зелеными и серыми вершинами равную k. Таким образом, между каждым мальчиком и каждой девочкой будет установлена связь через ребро либо напрямую, либо через вершины защитного уровня.

Танцы должны продолжаться в точности m раундов. Установим пропускную способность ребер между истоком и красными вершинами, а также между серыми вершинами и стоком равную m.

Graph

Находим максимальный поток в графе. Танцы могут продолжаться в точности m раундов тогда и только тогда, когда величина максимального потока будет равна n * m, где n – количество мальчиков.

При построении матрицы пропускных способностей g вершины графа имеют следующие номера:

·        исток: номер 0;

·        красные вершины: от 1 до n;

·        синие вершины: от n + 1 до 2*n;

·        синие вершины: от 2*n + 1 до 3*n;

·        синие вершины: от 3*n + 1 до 4*n;

·        сток: номер 4*n + 1;

 

 

ПРОГРАММА

 

#include <cstdio>

#include <string>

#include <vector>

#include <memory>

using namespace std;

#define MAX 52

 

int g[4*MAX][4*MAX],used[4*MAX];

int n,flow,MaxFlow;

 

int aug(int x,int t,int CurFlow)

{

  if (x == t) return CurFlow;

  if (used[x]++) return 0;

 

  for (int Flow,y = 0; y <= 4*n+1; y++)

  {

    if (g[x][y] > 0 && (Flow = aug(y,t,min(CurFlow,g[x][y]))))

    {

      g[x][y] -= Flow; g[y][x] += Flow;

      return Flow;

    }

  }

  return 0;

}

 

int CanDance(int m,int k,vector<string> &likes)

{

  int i,j;

  for(i=1;i<=n;i++) g[0][i] = g[3*n+i][4*n+1] = m;

  for(i=1;i<=n;i++) g[i][n+i] = g[2*n+i][3*n+i] = k;

  for(i=0;i<n;i++)

    for(j=0;j<n;j++)

      if (likes[i][j] == 'Y') g[i+1][3*n+j+1] = 1; else g[n+i+1][2*n+j+1] = 1;

 

  MaxFlow = 0;

  do

  {

    memset(used,0,sizeof(used));

  } while ((flow = aug(0,4*n+1,0x7FFFFFFF)) && (MaxFlow += flow));

  return MaxFlow == m*n;

}

 

class DancingParty

{

public:

  int maxDances(vector<string> likes, int k)

  {

    int m,low=0,high;

    high=n=(int)likes.size();

    memset(g,0,sizeof(g));

    while(low<high)

    {

      m = (low+high+1)/2;

      if(CanDance(m,k,likes)) low = m; else high = m-1;

    }

    return low;

  }

};